package com.demo.cms.filter;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.tuckey.web.filters.urlrewrite.extend.RewriteMatch;
import org.tuckey.web.filters.urlrewrite.extend.RewriteRule;
import org.tuckey.web.filters.urlrewrite.utils.Log;

import com.demo.Constants;
import com.demo.common.StringUtils;
import com.demo.core.model.Site;
import com.demo.webapp.context.MultisiteContextHolder;
import com.demo.webapp.filter.MultisiteFilter;

/**
 * 站点的URL重写规则
 * 
 * @author ryuu.kk
 *
 */
public class CmsRewriteRule extends RewriteRule {
	
	private static Log log = Log.getLog(CmsRewriteRule.class);

	private ServletContext servletContext;
	
	public static final String CMS_POSTFIX = ".html";
	
	// 排除指定后缀的请求地址
	private String[] extFilter = new String[]{".action", ".kc", ".", ".js", ".zip", ".css", ".jpg", ".jpeg", ".png", ".bmp", ".java", ".jsp", "/FormBasedXmlQueryDemo"};
	
	/**
	 * 检索页URL
	 */
	private static final String CMS_SEARCH_URL = "/user/Search.action";
	
	/**
	 * 文章URL
	 */
	private static final String CMS_ARTICLE_URL = "/user/Article.action";
	
	/**
	 * 栏目URL
	 */
	private static final String CMS_CATEGORY_URL = "/user/Category.action";
	
	/**
	 * 频道URL
	 */
	private static final String CMS_CHANNEL_URL = "/user/Channel.action";
	
	private static final String CMS_QUERY_CHANNEL = "channelPath";
	
	private static final String CMS_QUERY_CATEGORY = "categoryPath";
	
	private static final String CMS_QUERY_ARTICLE = "id";
	
	private static final String CMS_QUERY_CURRENTPAGE = "currentPage";
	
	 /**
	  * 规则初始化函数
	  * 如果返回false,则表示规则不可用
	  */
    public boolean initialise(ServletContext servletContext) {
    	this.servletContext = servletContext;
        return true;
    }

    /**
     * 规则匹配方法matches调用后被执行。
     * 用于析构
     */
    public void destroy() {
        // by default do nothing
    }

    /**
     * 当前请求的匹配对象,如果不匹配则返回null
     *
     * @param request HttpServletRequest
     * @param response HttpServletResponse
     */
    public RewriteMatch matches(HttpServletRequest request, HttpServletResponse response) {
    	
    	// 取得用户访问的站点模型
    	Site site = getSite(request);
    	// 请求的url后缀
    	String postfix = getSuffix(site, CMS_POSTFIX);
    	
    	// 得到请求地址
    	String requestUri = request.getServletPath();
    	int cnt = 2;
    	String[] point = requestUri.split("/") ;
    	// 如果匹配扩展名,则认为以站点前台访问||地址无扩展名且为短地址(频道地址/root或/root/)
    	if (requestUri.endsWith(postfix) 
    			|| (point.length == cnt && point[1].indexOf(".") < 0)) {
    		// 扩展名匹配
    		return new CMSRewriteMatch(site);
    	} 
    	// 如果不是站点的前台访问,则没有匹配当前请求,返回null.
        return null;
    }
    
    /**
     * CMS的Response封装对象
     * 重写URL
     * @author ryuu.kk
     *
     */
	private class CMSHttpServletResponseWrapper extends HttpServletResponseWrapper {

		private HttpServletResponse httpServletResponse;
		private HttpServletRequest httpServletRequest;
		private Site site;
		/**
		 * 请求地址后缀
		 */
		private String postfix;
		
		/**
		 * CMS的Response对象构造函数
		 * @param request HttpServletRequest
		 * @param response HttpServletResponse
		 */
		public CMSHttpServletResponseWrapper(HttpServletRequest request, HttpServletResponse response) {
			super(response);
			this.httpServletRequest = request; 
			this.httpServletResponse = response; 
			// 取得用户访问的站点模型
	    	site = getSite(request);
	    	postfix = site.getSuffix();
		}
		
		/**
		 * URL重写
		 * @param url String. cms前端站点中,需要重写的url地址
		 * @return String 重写后的url地址
		 */
		public String encodeURL(String url) {
			String baseUri = httpServletRequest.getContextPath();
			Map<String, String> params = new LinkedHashMap<String, String>();
			
			String targetUrl = baseUri;
			if (StringUtils.isNotEmpty(url)) {
				// 排除指定后缀的url地址
				for (String prefix : extFilter) {
					if (url.endsWith(prefix)) {
						//return super.encodeURL(url);
						// 排除地址将不做url编码
						return url;
					}
				}
				// 生成参数key
				if(url.indexOf("?") > 0) {
					String queryString = url.substring(url.indexOf("?") + 1);
					String[] args = queryString.split("&");
					for (String arg : args) {
						String[] p = arg.split("=");
						if (p != null && p.length > 1) {
							params.put(p[0], p[1]);
						}
					}
				}
				boolean sendError = false;
				// 得到固有参数
				String channel = params.remove(CMS_QUERY_CHANNEL);
				String categoty = params.remove(CMS_QUERY_CATEGORY);
				String id = params.remove(CMS_QUERY_ARTICLE);
				String currentPage = params.remove(CMS_QUERY_CURRENTPAGE);

				// 检索地址判断
				if (url.startsWith(baseUri + CMS_SEARCH_URL)) {
					if (StringUtils.isEmpty(channel)) {
						// 
						sendError = true;
					} 
					// 获得专有关键字
					String render = params.remove("templateId");
					String scope = params.remove("field");
					String query = params.remove("query");
					if (StringUtils.isNotEmpty(query)) {
						try {
							query = new String(query.trim().getBytes("ISO-8859-1"), "UTF-8");
						} catch (UnsupportedEncodingException e) {
							e.printStackTrace();
						}
						params.put("q", query);
					}
					
					if (StringUtils.isNotEmpty(scope)) {
						params.put("scope", scope);
					}
					params.put("render", render);
					params.put("p", currentPage);
					// 清空分页字符串
					currentPage = "";
					targetUrl += '/' + channel + "/s";
				} else if (url.startsWith(baseUri + CMS_CHANNEL_URL)) {
					if (StringUtils.isEmpty(channel)) {
						sendError = true;
					}
					targetUrl += '/' + channel + '/' + Constants.CMS_CHANNEL_DEFAULT;
				} else if (url.startsWith(baseUri + CMS_CATEGORY_URL)) {
					if (StringUtils.isEmpty(channel) || StringUtils.isEmpty(categoty)) {
						sendError = true;
					}
					targetUrl += '/' + channel + '/' + categoty;
				} else if (url.startsWith(baseUri + CMS_ARTICLE_URL)) {
					
					if (StringUtils.isEmpty(channel) || StringUtils.isEmpty(categoty) || StringUtils.isEmpty(id)) {
						sendError = true;
					}
					targetUrl += '/' + channel + '/' + categoty + '/' + Long.toHexString(Long.parseLong(id));
				}  else {
					// 不属于以上判断内容
					//targetUrl = url;
					return super.encodeURL(url);
				}
				if (sendError) {
					try {
						httpServletResponse.sendError(SC_NOT_FOUND);
						return null;
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
				// 处理分页参数
				if (StringUtils.isNotEmpty(currentPage)) {
					targetUrl += "-" + currentPage;
				}
				targetUrl += postfix;
				// 处理剩余参数
				if (!params.isEmpty()) {
					if (targetUrl.indexOf("?") < 0) {
						targetUrl += "?";
					}
					for (String key : params.keySet()) {
						targetUrl += key + "=" + params.get(key) + "&";
					}
					if (targetUrl.charAt(targetUrl.length() - 1) == '&') {
						targetUrl = targetUrl.substring(0, targetUrl.length() - 1);
					} 
				}
			}
			return super.encodeURL(targetUrl);
	    }

	    public String encodeRedirectURL(String url) {
	    	return super.encodeRedirectURL(url);
	    }

	    public String encodeUrl(String s) {
	    	return httpServletResponse.encodeURL(s);
	    }

	    public String encodeRedirectUrl(String url) {
	    	return super.encodeRedirectURL(url);
	    }
	}
	
	protected boolean isSearch(String[] args) {
    	if (args != null) {
    		// 默认频道 || 指定频道
    		if (args.length == 3) {
    			String act = args[args.length - 1];
    			return (act.length() == 1 && act.charAt(0) == 's');
    		}
    	}
    	return false;
    }
	
    protected boolean isChannel(String[] args, String suffix) {
    	if (args != null) {
    		return args.length == 2 || (args.length == 3 && args[2].equals(Constants.CMS_CHANNEL_DEFAULT));
    	}
    	return false;
    }
    
    protected boolean isCategory(String[] args, String suffix) {
    	if (args != null) {
    		return args.length == 3;
    	}
    	return false;
    }
    
    protected boolean isArticle(String[] args, String suffix) {
    	if (args != null) {
    		return (args.length == 4 || args.length == 5);
    	}
    	return false;
    }
    
	private RequestDispatcher getRequestDispatcher(final HttpServletRequest hsRequest, String toUrl, ServletContext targetContext) throws ServletException {
		String target = toUrl;
		final RequestDispatcher rq = (targetContext != null) ? targetContext
				.getRequestDispatcher(target) : hsRequest
				.getRequestDispatcher(toUrl);
		if (rq == null) {
			// this might be a 404 possibly something else, could re-throw a 404
			// but is best to throw servlet exception
			throw new ServletException("unable to get request dispatcher for " + toUrl);
		}
		return rq;
	}
	
	/**
	 * 获得站点模型 HttpServletRequest
	 * @param request
	 * @return Site
	 */
	private Site getSite(HttpServletRequest request) {
		// 获得当前线程中的站点模型
    	Site site = MultisiteContextHolder.getSite();
    	// 未找到线程模型,重新创建一个.
    	if (site == null) {
    		site = MultisiteFilter.createSite(request);
    	}
    	// 未找到匹配的站点,url重写结束
    	if (site == null) {
    		return null;
    	}
    	return site;
	}
	
	/**
	 * 获得url后缀
	 * @param site 站点模型
	 * @param defaultValue 默认值
	 * @return String
	 */
	private String getSuffix(Site site, String defaultValue) {
    	String suffix = site.getSuffix();
    	if (StringUtils.isNotEmpty(suffix)) {
    		if (suffix.charAt(0) != '.') {
    			suffix = '.' + suffix;
    		}
    	} else {
    		// 使用默认后缀
    		suffix = defaultValue;
    	}
    	return suffix;
	}
	
	private class CMSRewriteMatch extends RewriteMatch {
    	
		/**
		 * 站点模型
		 */
		private Site site;
		/**
		 * 构造函数 
		 * @param site Site
		 */
		private CMSRewriteMatch(Site site) {
			this.site = site;
		}
		
    	private String matchingUrl = "";
    	boolean requestRewritten = false; 
    	
		public String getMatchingUrl() {
			return this.matchingUrl;
		}

		public boolean execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
			
			String servletPath = "";
			String baseUri = request.getServletPath();
	    	String suffix = getSuffix(site, CMS_POSTFIX);
			// 去掉后缀
	    	if (baseUri.endsWith(suffix)) {
	    		baseUri = baseUri.substring(0, baseUri.length() - suffix.length());
	    	}
			
			String target = "";
			String[] args = baseUri.split("/");
			String currentPage = "";
//			for (String prefix : extFilter) {
//				if (baseUri.endsWith(prefix)) {
//					return false;
//				}
//			}
			if (args == null) {
				// 空地址,进入首页
			} else {
				String category = "";
				String article = "";
				//通过文章ID判断URL类型位置
				for(int i = 0; i < args.length; i ++) {
					// args[i].matches("[0-9a-f]+$"); // 整数
				}
				
				/***********************************************
				 * 默认频道首页:
				 * http://x.y.z/
				 * 
				 * 检索页
				 * http://x.y.z/channel-code/s?p=&....
				 * 
				 * 频道首页
				 * http://x.y.z/channel-code+postfix
				 * 
				 * 频道栏目页
				 * http://x.y.z/channel-code/category-path
				 * 
				 * 文章页
				 * http://x.y.z/id
				 * http://x.y.z/channel-code/category-path/id
				 * http://x.y.z/channel-code/category-path/id/
				 * http://x.y.z/channel-code/category-path/id/article-code
				 *  
				 * ******************************************** */
				if (isSearch(args)) {
					target = servletPath + CMS_SEARCH_URL + "?" + CMS_QUERY_CHANNEL + "=" + (args[1] == null ? "" : args[1]);
					// 参数重组
					// 页码
					String page = request.getParameter("p");
					// 渲染模板ID
					String render = request.getParameter("render");
					// 关键字
					String query = request.getParameter("q");
					// 查询范围
					String scope = request.getParameter("scope");
					if (StringUtils.isNotEmpty(scope)) {
						target += "&field=" + scope;
					}
					target += "&query=" + query + "&currentPage=" + page + "&templateId=" + render;
				} else if (isChannel(args, suffix)) {
					// 频道首页地址
					target = servletPath + CMS_CHANNEL_URL + "?" + CMS_QUERY_CHANNEL + "=" + (args[1] == null ? "" : args[1]);
				} else if (isCategory(args, suffix)) {
					// 栏目首页地址
					target = servletPath + CMS_CATEGORY_URL + "?" + CMS_QUERY_CHANNEL + "=" + (args[1] == null ? "" : args[1]) + "&categoryPath=";
					// 去掉"后缀"
					category = args[2];
					if (category.indexOf("-") > 0) {
						// 存在分页标志
						String[] arts = category.split("-");
						category = arts[0];
						currentPage = arts[1];
					}
					target += (category == null ? "" : category);
					if (StringUtils.isNotEmpty(currentPage)) {
						target += "&currentPage=" + currentPage;
					}
				} else if (isArticle(args, suffix)) {
					Long id  = null;
					article = args[3];
					if (article.indexOf("-") > 0) {
						// 存在分页标志
						String[] arts = article.split("-");
						article = arts[0];
						currentPage = arts[1];
					} 
					// 14-15位16进制数-> 17为long型ID
					if (article.matches("^[0-9a-fA-F]{14,15}")) {
						id = Long.parseLong(article, 16);
					}
					// 文章页
					target = servletPath + CMS_ARTICLE_URL + "?" + CMS_QUERY_CHANNEL + "=" + (args[1] == null ? "" : args[1]) + "&categoryPath=" + (args[2] == null ? "" : args[2]) + "&id=" + id;
					if (StringUtils.isNotEmpty(currentPage)) {
						target += "&currentPage=" + currentPage;
					}
				} else {
					target = servletPath + CMS_CHANNEL_URL;
				}
			}
			
			if (response.isCommitted()) {
	            log.error("response is comitted cannot forward to " + target +
	                    " (check you haven't done anything to the response (ie, written to it) before here)");
	        } else {
	      		RequestDispatcher rq;
				try {
					rq = getRequestDispatcher(request, target, servletContext);
					rq.forward(request, new CMSHttpServletResponseWrapper(request, response));
				} catch (ServletException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
	            if (log.isTraceEnabled()) log.trace("forwarded to " + target);
	        }
	        requestRewritten = true;
	        
	        return requestRewritten;
		}
	};
}

